/* CoralCube | Massively Multiplayer Online Role-Playing Game(MMORPG) Emulator.
|* 
|* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
|*               2008-2010 TrinityCore <http://www.trinitycore.org/>
|*               2008-2010 CoralCube <http://www.CoralCube.com/>
|*
|* This program is free software. You can redistribute it and/or modify it under the terms of 
|* the GNU General Public License as published by the Free Software Foundation, either 
|* version 2 of the License.
|*
|* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, 
|* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
|* See the GNU General Public License for more details.
|*
|* You should have received a copy of the GNU General Public License along with this program. 
|* If not, see <http://www.gnu.org/licenses/>.
|*/

#ifndef RIBONCORE_PET_H
#define RIBONCORE_PET_H

#include "ObjectDefines.h"
#include "Unit.h"
#include "TemporarySummon.h"

enum PetType
{
    SUMMON_PET              = 0,
    HUNTER_PET              = 1,
    MAX_PET_TYPE            = 4,
};

extern char const* petTypeSuffix[MAX_PET_TYPE];

#define MAX_PET_STABLES         4

// stored in character_pet.slot
enum PetSaveMode
{
    PET_SAVE_AS_DELETED        = -1,                        // not saved in fact
    PET_SAVE_AS_CURRENT        =  0,                        // in current slot (with player)
    PET_SAVE_FIRST_STABLE_SLOT =  1,
    PET_SAVE_LAST_STABLE_SLOT  =  MAX_PET_STABLES,          // last in DB stable slot index (including), all higher have same meaning as PET_SAVE_NOT_IN_SLOT
    PET_SAVE_NOT_IN_SLOT       =  100                       // for avoid conflict with stable size grow will use 100
};

enum HappinessState
{
    UNHAPPY = 1,
    CONTENT = 2,
    HAPPY   = 3
};

enum PetSpellState
{
    PETSPELL_UNCHANGED = 0,
    PETSPELL_CHANGED   = 1,
    PETSPELL_NEW       = 2,
    PETSPELL_REMOVED   = 3
};

enum PetSpellType
{
    PETSPELL_NORMAL = 0,
    PETSPELL_FAMILY = 1,
    PETSPELL_TALENT = 2,
};

struct PetSpell
{
    ActiveStates active;
    PetSpellState state;
    PetSpellType type;
};

enum ActionFeedback
{
    FEEDBACK_NONE            = 0,
    FEEDBACK_PET_DEAD        = 1,
    FEEDBACK_NOTHING_TO_ATT  = 2,
    FEEDBACK_CANT_ATT_TARGET = 3
};

enum PetTalk
{
    PET_TALK_SPECIAL_SPELL  = 0,
    PET_TALK_ATTACK         = 1
};

enum PetNameInvalidReason
{
    // custom, not send
    PET_NAME_SUCCESS                                        = 0,

    PET_NAME_INVALID                                        = 1,
    PET_NAME_NO_NAME                                        = 2,
    PET_NAME_TOO_SHORT                                      = 3,
    PET_NAME_TOO_LONG                                       = 4,
    PET_NAME_MIXED_LANGUAGES                                = 6,
    PET_NAME_PROFANE                                        = 7,
    PET_NAME_RESERVED                                       = 8,
    PET_NAME_THREE_CONSECUTIVE                              = 11,
    PET_NAME_INVALID_SPACE                                  = 12,
    PET_NAME_CONSECUTIVE_SPACES                             = 13,
    PET_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS          = 14,
    PET_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END   = 15,
    PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME              = 16
};

typedef UNORDERED_MAP<uint32, PetSpell> PetSpellMap;
typedef std::vector<uint32> AutoSpellList;

#define HAPPINESS_LEVEL_SIZE        333000

#define ACTIVE_SPELLS_MAX           4

#define PET_FOLLOW_DIST  1.0f
#define PET_FOLLOW_ANGLE (M_PI/2)
#define PET_FOCUS_REGEN_INTERVAL 4 * IN_MILLISECONDS

class Player;

class Pet : public Guardian
{
public:
    explicit Pet(Player *owner, PetType type = MAX_PET_TYPE);
    virtual ~Pet();

    void AddToWorld();
    void RemoveFromWorld();

    PetType getPetType() const { return m_petType; }
    void setPetType(PetType type) { m_petType = type; }
    bool isControlled() const { return getPetType() == SUMMON_PET || getPetType() == HUNTER_PET; }
    bool isTemporarySummoned() const { return m_duration > 0; }

    bool IsPermanentPetFor(Player* owner);              // pet have tab in character windows and set UNIT_FIELD_PETNUMBER

    bool Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 pet_number);
    bool CreateBaseAtCreature(Creature* creature);
    bool CreateBaseAtCreatureInfo(CreatureInfo const* cinfo,Unit * owner);
    bool CreateBaseAtTamed(CreatureInfo const * cinfo, Map * map, uint32 phaseMask);
    bool LoadPetFromDB(Player* owner,uint32 petentry = 0,uint32 petnumber = 0, bool current = false);
    bool isBeingLoaded() const { return m_loading;}
    void SavePetToDB(PetSaveMode mode);
    void Remove(PetSaveMode mode, bool returnreagent = false);
    static void DeleteFromDB(uint32 guidlow);

    void setDeathState(DeathState s);                   // overwrite virtual Creature::setDeathState and Unit::setDeathState
    void Update(uint32 diff);                           // overwrite virtual Creature::Update and Unit::Update

    uint8 GetPetAutoSpellSize() const { return m_autospells.size(); }
    uint32 GetPetAutoSpellOnPos(uint8 pos) const
    {
        if (pos >= m_autospells.size())
            return 0;
        else
            return m_autospells[pos];
    }

    void LoseHappiness();
    HappinessState GetHappinessState();
    void GivePetXP(uint32 xp);
    void GivePetLevel(uint8 level);
    void SynchronizeLevelWithOwner();
    bool HaveInDiet(ItemPrototype const* item) const;
    uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel);
    void SetDuration(int32 dur) { m_duration = dur; }

    /*
    bool UpdateStats(Stats stat);
    bool UpdateAllStats();
    void UpdateResistances(uint32 school);
    void UpdateArmor();
    void UpdateMaxHealth();
    void UpdateMaxPower(Powers power);
    void UpdateAttackPowerAndDamage(bool ranged = false);
    void UpdateDamagePhysical(WeaponAttackType attType);
    */

    bool CanTakeMoreActiveSpells(uint32 SpellIconID);
    void ToggleAutocast(uint32 spellid, bool apply);

    bool HasSpell(uint32 spell) const;

    void LearnPetPassives();
    void CastPetAuras(bool current);
    void CastPetAura(PetAura const* aura);
    bool IsPetAura(Aura const* aura);

    void _LoadSpellCooldowns();
    void _SaveSpellCooldowns(SQLTransaction& trans);
    void _LoadAuras(uint32 timediff);
    void _SaveAuras(SQLTransaction& trans);
    void _LoadSpells();
    void _SaveSpells(SQLTransaction& trans);

    bool addSpell(uint32 spell_id,ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL);
    bool learnSpell(uint32 spell_id);
    void learnSpellHighRank(uint32 spellid);
    void InitLevelupSpellsForLevel();
    bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
    bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
    void CleanupActionBar();

    PetSpellMap     m_spells;
    AutoSpellList   m_autospells;

    void InitPetCreateSpells();

    bool resetTalents(bool no_cost = false);
    static void resetTalentsForAllPetsOf(Player* owner, Pet* online_pet = NULL);
    uint32 resetTalentsCost() const;
    void InitTalentForLevel();

    uint8 GetMaxTalentPointsForLevel(uint8 level);
    uint8 GetFreeTalentPoints() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); }
    void SetFreeTalentPoints(uint8 points) { SetByteValue(UNIT_FIELD_BYTES_1, 1, points); }

    uint32  m_resetTalentsCost;
    time_t  m_resetTalentsTime;
    uint32  m_usedTalentCount;

    const uint64& GetAuraUpdateMaskForRaid() const { return m_auraRaidUpdateMask; }
    void SetAuraUpdateMaskForRaid(uint8 slot) { m_auraRaidUpdateMask |= (uint64(1) << slot); }
    void ResetAuraUpdateMaskForRaid() { m_auraRaidUpdateMask = 0; }

    DeclinedName const* GetDeclinedNames() const { return m_declinedname; }

    bool    m_removed;                                  // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved)

    Player *GetOwner() { return m_owner; }
protected:
    Player *m_owner;
    uint32  m_happinessTimer;
    PetType m_petType;
    int32   m_duration;                                 // time until unsummon (used mostly for summoned guardians and not used for controlled pets)
    uint64  m_auraRaidUpdateMask;
    bool    m_loading;
    uint32  m_regenTimer;

    DeclinedName *m_declinedname;

private:
    void SaveToDB(uint32, uint8, uint32)                // override of Creature::SaveToDB     - must not be called
    {
        ASSERT(false);
    }
    void DeleteFromDB()                                 // override of Creature::DeleteFromDB - must not be called
    {
        ASSERT(false);
    }
};
#endif
